#pragma once
#include <iostream>
#include "Sem.hpp"
#include "Mutex.hpp"
#include <vector>
// 基于信号量
// 共享资源为环形队列
// 实现的生产者消费者模型
using namespace SemModule;
using namespace MutexModel;

const int defaultcap = 5;

// 基于环形队列 使用信号量
// 模型生产者消费者模型
// 生产者和消费者分别申请信号量，实现对资源的预定机制
// 重要的接口Enque生产者调用接口，Pop消费者调用接口，可以通过输出型参数的形式返回
template <class T>
class RunningQueue
{
public:
    RunningQueue(int cap = defaultcap)
        : _cap(cap),
          _rq(cap),
          _blank(cap),
          _data(0),
          p_step(0),
          c_step(0)
    {
    }

    // 生产者调用接口
    void Enque(const T &data)
    {
        // 生产者申请信号量，队列中空位置
        // P操作  P--
        // 申请失败，会阻塞在这里
        _blank.P();
        // 多线程线申请信号量，申请资源
        // 申请成功的多线程再竞争锁
        {
            LockGuard lockguard(_pmutex); // 加锁，生命周期结束自动释放锁
            //_pmutex.Lock();
            // 生产
            _rq[p_step] = data;
            ++p_step;
            p_step %= _cap;
            //_pmutex.Unlock();
        }
        _data.V();
    }
    // 消费者调用接口
    void Pop(T *out)
    {
        // 消费者申请信号量
        // P操作 P--
        _data.P();
        {
            LockGuard lockguard(_cmutex);
            //_cmutex.Lock();
            *out = _rq[c_step];
            ++c_step;
            c_step %= _cap;
            //_cmutex.Unlock();
        }
        _blank.V();
    }

    // //生产者调用接口
    // void Enque(const T& data)
    // {
    //     //生产者申请信号量，队列中空位置
    //     //P操作  P--
    //     //申请失败，会阻塞在这里
    //     _blank.P();
    //     //生产
    //     _rq[p_step]=data;
    //     p_step++;
    //     p_step%=_cap;
    //     _data.V();
    // }
    // //消费者调用接口
    // void Pop(T* out)
    // {
    //     //消费者申请信号量
    //     //P操作 P--
    //     _data.P();
    //     *out=_rq[c_step];
    //     c_step++;
    //     c_step%=_cap;
    //     _blank.V();

    // }
    // ~RunningQueue()
    // {
    // }

private:
    std::vector<T> _rq; // 模拟环形队列
    int _cap;           // 信号量的大小，将资源划分为几个部分

    // 生产者 信号量
    Sem _blank; // 关注 队列中空位置的个数，初始化应该为_cap
    int p_step; // 生产者对应 下标位置

    // 消费者信号量
    Sem _data;  // 关注都列中存放数据位置的个数，初始化应该为0
    int c_step; // 消费者对应 下标位置
    // 通过信号量，可以完成单生产者单消费者的互斥与同步

    // 扩展为多生产者多消费者模型
    // 还缺少维护生产者之间，消费者之间的关系
    // 生产者和生产者之间是互斥的
    // 消费者和消费者之间是互斥的
    //互斥锁确保同一时间只有一个线程访问资源
    Mutex _cmutex;
    Mutex _pmutex;
};